//
//  FileChangeMonitor.m
//  OatosIOSClient
//
//  Created by YF-GY on 2016/12/15.
//  Copyright © 2016年 qycloud. All rights reserved.
//

#import "FileChangeMonitor.h"
@interface FileChangeMonitor()
@property (nonatomic,strong) NSMutableArray *fileStreamArr;
@property (nonatomic,strong) NSString *fileName;
@property (nonatomic,strong) NSString *filePath;
@property (nonatomic,assign) BOOL renamedFlag;
@property (nonatomic,assign) BOOL isPreview;
@end

@implementation FileChangeMonitor
{
    FSEventStreamRef stream;
}

static  FileChangeMonitor *selfClass = nil;

+(instancetype)monitorWithFileDic:(NSDictionary *)fileItem preview:(BOOL)isPreview
{
    FileChangeMonitor *monitor = [[FileChangeMonitor alloc]initWithFileDic:fileItem preview:isPreview];
    selfClass = monitor;
    return monitor;
}

-(instancetype)initWithFileDic:(NSDictionary *)fileItem preview:(BOOL)isPreview
{
    if (self = [super init]) {
        
//        NSString *fileName = nil;
//        if ([fileItem objectForKey:kOatosParamsName]) {
//            fileName = [fileItem objectForKey:kOatosParamsName];
//        }
//        //            NSMutableArray *parmaArray = [self.itemArr mutableCopy];
//        //查看文件
//        NSString *filePath = nil;
//        if (isPreview) {
//            filePath = [OatosUtils getFilePreViewPath:fileItem];
//        }
//        else
//        {
//            filePath = [OatosUtils getFileLocalPath:fileItem];
//        }
//        if (!fileName) {
//            fileName = [filePath lastPathComponent];
//        }
//        self.isPreview = isPreview;
//        self.fileName = fileName;
//        self.filePath = filePath;
//        self.fileDic = fileItem;
//        selfClass = self;
    }
    return self;
}

//-(NSString *)checkStatus
//{
//    NSString *fileName = self.fileName;
//    
//    NSString *directory = [self.filePath stringByDeletingLastPathComponent];
//    //The function checks whether the document “fileName” is open or not.
//    
//    //NSTask is used to run the terminal command from a the cocoa application
//    
//    NSTask *task;
//    
//    task = [[NSTask alloc] init];
//    
//    //Set the path at which document is present
//    
//    [task setCurrentDirectoryPath: directory];
//    
//    //Set terminal command which has to be run for determining whether the document is open or not
//    
//    [task setLaunchPath:@"/usr/sbin/lsof"];
//    
//    //Set the name of the document
//    
//    NSArray *arguments;
//    
//    arguments = [NSArray arrayWithObjects:fileName,nil];
//    
//    [task setArguments: arguments];
//    
//    NSPipe *pipe;
//    
//    pipe = [NSPipe pipe];
//    
//    [task setStandardOutput: pipe];
//    
//    NSFileHandle *file;
//    
//    file = [pipe fileHandleForReading];
//    
//    [task launch];
//    
//    NSData *data;
//    
//    data = [file readDataToEndOfFile];
//    
//    NSString *string;
//    
//    string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
//    
//    NSLog (@"result:\n%@", string);
//    
//    return string;
//}

#pragma mark- 开始监控文件是否关闭
- (void)startDetect
{
    dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue1, ^{
        [self detectFileLoop];
    });
}

#pragma mark-
- (void) detectFileLoop
{
    while (YES) {
        NSTask *task = [[NSTask alloc] init];
        [task setLaunchPath: @"/bin/bash"];
        NSString *yourFilePath = _filePath;
        [task setArguments:[NSArray arrayWithObjects: @"-c",[NSString stringWithFormat:@"%@ %@ %@",@"lsof",@"-t",yourFilePath],nil]];
        NSPipe *pipe;
        pipe = [NSPipe pipe];
        [task setStandardOutput: pipe];
        
        NSFileHandle *file = [pipe fileHandleForReading];
        [task launch];
        NSData *data = [file readDataToEndOfFile];
        NSString *response = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
        if ([response length] > 0)
        {
            NSLog(@"test.doc file has been opened and process id is %@",response);
        }
        else
        {
            NSLog(@"test.doc file has been closed");
            if ([self.delegate respondsToSelector:@selector(FileChangeMonitorFileclosed:)]) {
                [self.delegate FileChangeMonitorFileclosed:selfClass];
            }
            break;
        }
    }
}

-(void)startMonitor
{
    CFStringRef path = (__bridge CFStringRef)_filePath;
    
    CFArrayRef  paths= CFArrayCreate(NULL, (const void **)&path, 1, NULL);
    
    stream = FSEventStreamCreate(kCFAllocatorDefault,
                                                  
                                                  &eventStreamCallback,
                                                  
                                                  NULL,
                                                  
                                                  paths,
                                                  
                                                  kFSEventStreamEventIdSinceNow,
                                                  
                                                  1.0, // 事件发生后延迟多少秒调用回调，如果时间设长则有更高的效率，会一次性通知多个事件
                                                  
                                                  kFSEventStreamCreateFlagFileEvents) ;
    
    
    
    FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    
    FSEventStreamStart(stream);
    
    [self.fileStreamArr addObject:(__bridge id _Nonnull)(stream)];
    
    CFRunLoopRun();

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self startDetect];
    });
}

//－None：默认标识符。如果目录发生了特定的修改操作，工具不会为此操作设置事件标识符。
//
//－MustScanSubDirs：通知应用程序，需要重新扫描受影响的目录以及该目录下的所有子目录。
//
//－UserDropped：在设置标识符的时候发生错误，应用程序需要对目录执行完整的扫描操作。
//
//－KernelDropped：在设置标识符的时候发生错误，应用程序需要对目录执行完整的扫描操作。
//
//－EventldsWrapped：事件ID计数器发生错误，之前的事件ID将无效。
//
//－HistoryDone：设置相应的标识符来表示之前哪些标识符需要忽略。
//
//－RootChanged：当监视目录下发生修改操作时，设置相应的标识符。
//
//－Mount：加载一个需要进行监视的卷宗。
//
//－Unmount：卸载一个不需要进行监视的卷宗。
//
//－ItemCreated：当文件或目录创建成功之后，设置相应的标识符。
//
//－ItemRemoved：当文件或目录删除成功之后，设置相应的标识符。
//
//－ItemInodeMetaMod：当节点元数据发生变化时，设置相应的标识符。
//
//－ItemRenamed：当文件或目录名称修改成功之后，设置相应的标识符。
//
//－ItemModified：当文件或目录修改成功之后，设置相应的标识符。
//
//－ItemFinderInfoMod：文件finder的元数据被修改之后，设置相应的标识符。
//
//－ItemChangeOwner：当文件或目录的所有者发生变化时，设置相应的标识符。
//
//－ItemIsFile：操作对象是一个文件。
//
//－ItemIsDir：操作对象是一个目录。
//
//－ItemIsSymlink：操作对象是一个符号链接。

void eventStreamCallback(
                         
                         ConstFSEventStreamRef streamRef,
                         
                         void *clientCallBackInfo,
                         
                         size_t numEvents,
                         
                         void *eventPaths,
                         
                         const FSEventStreamEventFlags eventFlags[],
                         
                         const FSEventStreamEventId eventIds[])

{
    
    int i;
    
    
    /* eventPaths
     
     An array of paths to the directories in which event(s) occurred.
     
     The type of this parameter depends on the flags passed to FSEventStreamCreate...(). If kFSEventStreamCreateFlagUseCFTypes was set, then this will be a CFArrayRef containing CFStringRef objects (per CFStringCreateWithFileSystemRepresentation()). Ownership follows the Get rule, and they will be released by the framework after your callback returns. If kFSEventStreamCreateFlagUseCFTypes was not set, then the framework will pass your callback a raw C array of raw C strings that will be deallocated by the framework after your callback returns.
     
     A path might be '/' if ether of these flags is set for the event: kFSEventStreamEventFlagUserDropped, kFSEventStreamEventFlagKernelDropped.
     */
    
    char **paths = eventPaths;
    
    for (i=0; i<numEvents; i++)
    {
        NSString *filePath = [NSString stringWithCString:paths[i] encoding:NSUTF8StringEncoding];
        NSLog(@"path=%@", filePath);
        if (selfClass.renamedFlag&&![filePath isEqualToString:selfClass.filePath]) {
            selfClass.redifinedfileName = [filePath lastPathComponent];
            if ([selfClass.delegate respondsToSelector:@selector(FileChangeMonitorFileRenamed:)]) {
                [selfClass.delegate FileChangeMonitorFileRenamed:selfClass];
            }
        }
        else if ([filePath isEqualToString:selfClass.filePath])
        {
            if (eventFlags[i] &kFSEventStreamEventFlagItemIsFile)
            {
                NSLog(@"kFSEventStreamEventFlagItemIsFile");
                if ([selfClass.delegate respondsToSelector:@selector(FileChangeMonitorFileUpdated:)]) {
                    [selfClass.delegate FileChangeMonitorFileUpdated:selfClass];
                }
                
                if (eventFlags[i] &kFSEventStreamEventFlagItemRenamed)
                {
                    NSLog(@"kFSEventStreamEventFlagItemRenamed");
                }
            }
        }
        else
        {
            return;
            if (eventFlags[i] &kFSEventStreamEventFlagItemRemoved)
            {
                NSLog(@"kFSEventStreamEventFlagItemRemoved");
//                if ([selfClass.delegate respondsToSelector:@selector(FileChangeMonitorFileRemoved:)]) {
//                    [selfClass.delegate FileChangeMonitorFileRemoved:selfClass];
//                }
            }
            if (eventFlags[i] &kFSEventStreamEventFlagRootChanged)
            {
                NSLog(@"kFSEventStreamEventFlagRootChanged");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagMustScanSubDirs)
            {
                NSLog(@"kFSEventStreamEventFlagMustScanSubDirs");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagUserDropped)
            {
                NSLog(@"kFSEventStreamEventFlagUserDropped");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagKernelDropped)
            {
                NSLog(@"kFSEventStreamEventFlagKernelDropped");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagEventIdsWrapped)
            {
                NSLog(@"kFSEventStreamEventFlagEventIdsWrapped");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagHistoryDone)
            {
                NSLog(@"kFSEventStreamEventFlagHistoryDone");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagRootChanged)
            {
                NSLog(@"kFSEventStreamEventFlagRootChanged");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagMount)
            {
                NSLog(@"kFSEventStreamEventFlagMount");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagUnmount)
            {
                NSLog(@"kFSEventStreamEventFlagUnmount");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagItemCreated)
            {
                NSLog(@"kFSEventStreamEventFlagItemCreated");
            }
            
            if (eventFlags[i] &kFSEventStreamEventFlagItemInodeMetaMod)
            {
                NSLog(@"kFSEventStreamEventFlagItemInodeMetaMod");
            }

            if (eventFlags[i] &kFSEventStreamEventFlagItemModified)
            {
                NSLog(@"kFSEventStreamEventFlagItemModified");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagItemFinderInfoMod)
            {
                NSLog(@"kFSEventStreamEventFlagItemFinderInfoMod");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagItemChangeOwner)
            {
                NSLog(@"kFSEventStreamEventFlagItemChangeOwner");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagItemXattrMod)
            {
                NSLog(@"kFSEventStreamEventFlagItemXattrMod");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagItemIsDir)
            {
                NSLog(@"kFSEventStreamEventFlagItemIsDir");
                
            }
            if (eventFlags[i] &kFSEventStreamEventFlagItemIsSymlink)
            {
                NSLog(@"kFSEventStreamEventFlagItemIsSymlink");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagOwnEvent)
            {
                NSLog(@"kFSEventStreamEventFlagOwnEvent");
            }
            if (eventFlags[i] &kFSEventStreamEventFlagItemIsHardlink)
            {
                NSLog(@"kFSEventStreamEventFlagItemIsHardlink");
            }
        }
    }
    FSEventStreamStop((FSEventStreamRef)streamRef);
}


@end
